package store.gn998.stream;

import org.junit.jupiter.api.Test;
import store.gn998.stream.bean.Person;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author 郭泽鹏
 * @Date 2021/11/8 11:04
 * @Version 1.0
 * @Description Stream流的使用
 */
public class StreamUse {
    public static List<Person> personList = new ArrayList<Person>();

    static {
        personList.add(new Person("Tom", 8900, 18,"male", "New York"));
        personList.add(new Person("Jack", 7000, 22, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 20, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 16, "female", "New York"));
        personList.add(new Person("Owen", 9500, 29, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 20, "female", "New York"));
    }

    /**
     * 匹配 foreach/find/match
     */
    @Test
    public void test01() {
        //遍历输出符合条件的元素
        personList.stream().filter(person -> person.getAge() > 18).forEach(System.out::println);
        //匹配第一个
        Optional<Person> first = personList.stream().filter(person -> person.getAge() > 18).findFirst();
        //匹配任意一个（适用于并行流）
        Optional<Person> any = personList.parallelStream().filter(person -> person.getAge() > 18).findAny();
        //是否包含符合特定条件的元素
        boolean anyMatch = personList.stream().anyMatch(person -> person.getAge() == 18);
        System.out.println("匹配第一个元素>>>" + first);
        System.out.println("匹配任意一个（适用于并行流）>>>" + any);
        System.out.println("是否包含符合特定条件的元素>>>" + anyMatch);
    }

    /**
     * 筛选filter
     */
    //案例一：筛选出Integer集合中大于7的元素，并打印出来
    @Test
    public void test02(){
        List<Integer> list = Arrays.asList(6, 9, 1, 3, 5, 10, 7, 25);
        Stream<Integer> stream = list.stream().filter(x -> x > 7);
        stream.forEach(System.out::println);
    }

    //案例二：筛选员工中工资高于8000的人，并形成新的集合，形成新集合依赖collect（收集）
    @Test
    public void test03() {
        List<String> collect = personList.stream()
                .filter(person -> person.getSalary() > 8000)
                .map(Person::getName)
                .collect(Collectors.toList());
        System.out.println("筛选员工中工资高于8000的人" + collect);
    }

    /**
     * 聚合
     */
    //案例一：获取名字最长的员工姓名
    @Test
    public void test04() {
        Optional<String> max = personList.stream()
                .map(Person::getName)
                .max(Comparator.comparing(String::length));
        System.out.println(max.get());
    }

    //案例二：获取Integer集合中的最大值
    @Test
    public void test05() {
        List<Integer> list = Arrays.asList(16, 3, 8, 4, 6, 12, 25, 5, 18);

        //方式一：自然排序
        Optional<Integer> max2 = list.stream().max(Integer::compareTo);
        System.out.println("方式一：自然排序最大值:" + max2.get());

        //方式二：自定义排序
        Optional<Integer> max = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        System.out.println("方式二：自定义排序最大值" + max.get());

        //方式三：自定义排序
        Optional<Integer> max1 = list.stream().max((x1, x2) -> x1 - x2);
        System.out.println("方式三：自定义排序最大值:" + max1.get());
    }

    //案例三：获取员工工资最高的人
    @Test
    public void test06() {
        Optional<Person> max = personList.stream().max(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getSalary() - o2.getSalary();
            }
        });
        System.out.println("获取员工工资最高的人：" + max.get());

        Optional<Person> min = personList.stream().min((p1, p2) -> p1.getSalary() - p2.getSalary());
        System.out.println("获取员工工资最低的人：" + min.get());
    }

    //案例四：获取integer集合中大于6的元素的个数
    @Test
    public void test07() {
        List<Integer> list = Arrays.asList(16, 3, 8, 4, 6, 12, 25, 5, 18);
        long count = list.stream().filter(x -> x > 6).count();
        System.out.println("获取integer集合中大于6的元素的个数:" + count);
    }

    /**
     * 映射map/flatMap
     */
    //案例一：英文字符串数组的元素全部改为大写，整数数组每个元素+3
    @Test
    public void test08() {
        String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
        List<String> collect = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("英文字符串数组的元素全部改为大写:" + collect);

        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        List<Integer> collect1 = intList.stream().map(num -> num + 3).collect(Collectors.toList());
        System.out.println("整数数组每个元素+3:" + collect1);
    }

    //案例二:将员工的薪资全部增加1000
    @Test
    public void test09() {
        //第一种方式：不改变原本的员工集合
        List<Person> collect = personList.stream()
                .map(person -> {
                    Person p1 = null;
                    try {
                        p1 = (Person) person.clone();
                        p1.setSalary(p1.getSalary() + 1000);
                    } catch (CloneNotSupportedException e) {
                        e.printStackTrace();
                    }
                    return p1;
                })
                .collect(Collectors.toList());
        System.out.println("第一种方式：不改变原本的员工集合：");
        System.out.println(collect);
        System.out.println("--------------------------------------------------------");

        //第二种方式：改变原本的员工集合
        List<Person> collect1 = personList.stream().map(person -> {
            person.setSalary(person.getSalary() + 1000);
            return person;
        }).collect(Collectors.toList());
        System.out.println("第二种方式：改变原本的员工集合:");
        System.out.println(collect1);
    }

    //案例三：将两个字符串合并成一个新的字符串
    //flatMap：对映射出来的多个流进行合并操作
    @Test
    public void test10() {
        List<String> list = Arrays.asList("m-k-l-a", "1-3-5-7");
        List<String> newList = list.stream().flatMap(s -> {
            String[] split = s.split("-");
            Stream<String> stream = Arrays.stream(split);
            return stream;
        }).collect(Collectors.toList());
        System.out.println("原先的list集合：" + list);
        System.out.println("处理之后的集合：" + newList);
    }

    /**
     * 规约reduce
     */
    //案例一：求integer集合的元素之和，乘积和最大值
    @Test
    public void test11() {
        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        //第一种方式：
        Optional<Integer> sum1 = intList.stream().reduce(Integer::sum);
        //第二种方式：
        Optional<Integer> sum2 = intList.stream().reduce((sum, x) -> sum + x);
        //第三种方式
        Integer sum3 = intList.stream().reduce(0, (sum, x) -> sum + x);
        System.out.println("求集合的元素之和：\t1、" + sum1.get() + "\t2、" + sum2.get() + "\t3、" + sum3);

        Optional<Integer> multi = intList.stream().reduce((x, y) -> x * y);
        System.out.println("求集合的元素乘积：" + multi.get());

        Optional<Integer> max = intList.stream().reduce(Integer::max);
        System.out.println("求集合的最大值：" + max.get());
    }

    //案例二：求所有员工的工资之和和最高工资
    @Test
    public void test12() {
        //第一种方式
        Optional<Integer> sum1 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        //第二种方式
        Integer sum2 = personList.stream().reduce(0, (sum, p) -> sum + p.getSalary(),
                (s1, s2) -> s1 + s2);
        //第三种方式
        Integer sum3 = personList.stream().reduce(0, (sum, person) -> sum + person.getSalary(), Integer::sum);
        System.out.println("求员工的工资之和：\t1、" + sum1.get() + "\t2、" + sum2 + "\t3、" + sum3);

        //第一种方式
        Optional<Integer> max1 = personList.stream().map(Person::getSalary).reduce(Integer::max);
        //第二种方式
        Integer max2 = personList.stream().reduce(0, (max, person) -> max > person.getSalary() ? max : person.getSalary(),
                Integer::max);
        //第三种方式
        Integer max3 = personList.stream().reduce(0, (max, person) -> max > person.getSalary() ? max : person.getSalary(),
                (m1, m2) -> m1 > m2 ? m1 : m2);
        System.out.println("最高工资是：\t1、" + max1.get() + "\t2、" + max2 + "\t3、" + max3);
    }

    /**
     * 收集collect
     */
    //简单使用toList、toMap、toSet
    @Test
    public void test13() {
        List<Integer> intList = Arrays.asList(1, 3, 5, 8, 9, 12);

        List<Integer> integerList = intList.stream()
                .filter(x -> x % 2 == 0)
                .collect(Collectors.toList());

        Set<Integer> integerSet = intList.stream()
                .filter(x -> x % 2 != 0)
                .collect(Collectors.toSet());

        Map<String, Person> personMap = personList.stream()
                .filter(person -> person.getSalary() > 8000)
                .collect(Collectors.toMap(Person::getName, person -> person));

        System.out.println("toList:" +integerList);
        System.out.println("-------------------------");

        System.out.println("toSet:" + integerSet);
        System.out.println("-------------------------");

        System.out.println("toMap:" + personMap);
    }

    //案例二：统计员工人数，平均工资，工资总额，最高工资
    @Test
    public void test14() {
        Long count = personList.stream()
                .collect(Collectors.counting());

        Double average = personList.stream()
                .collect(Collectors.averagingDouble(Person::getSalary));

        Integer sum = personList.stream()
                .collect(Collectors.summingInt(Person::getSalary));

        Optional<Integer> max = personList.stream()
                .map(Person::getSalary)
                .collect(Collectors.maxBy(Integer::compareTo));

        //一次性统计所有信息
        DoubleSummaryStatistics summaryStatistics = personList.stream()
                .collect(Collectors.summarizingDouble(Person::getSalary));

        System.out.println("员工总数为：" + count);
        System.out.println("平均工资为：" + average);
        System.out.println("工资总和为：" + sum);
        System.out.println("最高工资为：" + max.get());
        System.out.println("一次性统计所有信息" + summaryStatistics);
    }

    //案例三：将员工按照薪资是否高于8000分为两部分；将员工按性别和地区进行分组
    @Test
    public void test15() {
        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> listMap = personList.stream()
                .collect(Collectors.partitioningBy(person -> person.getSalary() > 8000));

        //将员工按性别分组，再按地区分组
        Map<String, Map<String, List<Person>>> collect = personList.stream().collect(
                Collectors.groupingBy(Person::getSex,
                        Collectors.groupingBy(Person::getArea)));
        System.out.println("将员工按薪资是否高于8000分组：" + listMap);

        System.out.println("将员工先按性别分组，再按地区分组：" + collect);
    }

    //案例四：接合joining，通过“-”拼接所有员工的姓名
    @Test
    public void test16() {
        String collect = personList.stream()
                .map(Person::getName)
                .collect(Collectors.joining("-"));
        System.out.println("所有员工的姓名：" + collect);
    }

    //案例五：规约reduce
    @Test
    public void test17() {
        Integer collect = personList.stream().
                collect(Collectors
                        .reducing(0, Person::getSalary, (sum, i) -> (sum + i - 5000)));

        System.out.println("员工扣税薪资总和：" + collect);

        Optional<Integer> sum = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        System.out.println("员工未扣税薪资总和：" + sum.get());
    }

    /**
     * 排序sort
     */
    //案例一：将员工按工资由低到高排序
    @Test
    public void test18() {
        //自然排序【倒序，由高到低】
        List<String> names1 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName)
                .collect(Collectors.toList());
        System.out.println("自然排序【倒序，由高到低】:" + names1);

        //自定义排序
        List<String> names = personList.stream()
                .sorted((p1, p2) -> (p1.getSalary() - p2.getSalary()))
                .map(Person::getName)
                .collect(Collectors.toList());
        System.out.println("自定义排序【顺序】：" + names);

        //自定义排序【先按工资，再按年龄排序】升序
        List<String> collect = personList.stream().sorted((p1, p2) -> {
            if (p1.equals(p2)) {
                return 0;
            } else if (p1.getSalary() == p2.getSalary()) {
                return p1.getAge() - p2.getAge();
            } else {
                return p1.getSalary() - p2.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());
        System.out.println("定义排序【先按工资，再按年龄排序】升序:" + collect);
    }

    /**
     * 提取/组合
     */
    @Test
    public void test19() {
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> arr1Stream = Stream.of(arr1);
        Stream<String> arr2Stream = Stream.of(arr2);
        //concat:合并两个流 distinct：去重
        List<String> newList = Stream.concat(arr1Stream, arr2Stream)
                .distinct()
                .collect(Collectors.toList());
        // limit：限制从流中获得前n个数据
        List<Integer> collect = Stream.iterate(1, x -> x + 2)
                .limit(10)
                .collect(Collectors.toList());
        // skip：跳过前n个数据
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2)
                .skip(1)
                .limit(5)
                .collect(Collectors.toList());
        System.out.println("合并两个数组并去重：" + newList);
        System.out.println("limit：" + collect);
        System.out.println("skip：" + collect2);
    }
}


